/** @file    GuiUtilities.cpp
 *  @author  Alessandro Polo
 *  @version $Id: GuiUtilities.cpp 2883 2010-08-09 14:51:56Z alex $
 *  @brief
 * File containing methods for the wosh::gui::GuiUtilities class.
 * The header for this class can be found in GuiUtilities.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "GuiUtilities.h"

 #include <core/ObjectFactory.h>
 #include <core/List.h>
 #include <core/Response.h>
 #include <core/PersistenceManager.h>

 #include "FormContainer.h"
 #include "WoshTableModel.h"

 #include <QMessageBox>
 #include <QDir>


using namespace std;
namespace wosh {
 namespace gui {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

GuiSession::GuiSession()
	: wosh::communication::SessionProxy() {
	Object::setName("GuiSession", getID());
	Log.setContext( Object::getName(), true );

 }

GuiSession::~GuiSession() {

 }

SecurityToken* GuiSession::createToken( const Object* creator) {
	MutexLockerRead(&this->loggingLock);
	if ( this->credentials == NULL ) return NULL;
	return this->credentials->createToken( creator, this->username );
 }

SecurityToken* GuiSession::signMessage( Message* message ) {
	MutexLockerRead(&this->loggingLock);
	if ( this->credentials == NULL ) return NULL;
	return this->credentials->signMessage( message, this->username );
 }

SecurityToken* GuiSession::signMessage( const Object* creator, Message* message ) {
	MutexLockerRead(&this->loggingLock);
	if ( this->credentials == NULL ) return NULL;
	return this->credentials->signMessage( message, creator );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

ObjectTypeInfo GuiUtilities::findGuiFor( const std::string& type, double version, bool neareast ) {
	ObjectTypeInfo info;
	GuiMappings::getMappings().transactionBeginRead();
	if ( GuiMappings::getMappings().exists(type) )
		info = GuiMappings::getMappings().find( type );
	else if ( neareast ) {
		std::vector<ObjectTypeInfo> class_list;
		WRESULT ret = ObjectFactory::gatherInfoOf(class_list, type, version);
		if ( WFAILED(ret) || class_list.empty() )
			return info;
		std::vector<ObjectTypeInfo>::const_iterator it;
		for (it=class_list.begin(); it!=class_list.end(); ++it) {
			if ( (*it).name == type ) continue;
			if ( !GuiMappings::getMappings().exists((*it).name) ) continue;
			info = GuiMappings::getMappings().find( (*it).name );
			break;
		 }
	 }
	GuiMappings::getMappings().transactionEnd();
	return info;
 }

IReflection* GuiUtilities::createGuiFor( const std::string& type, double version ) {
	ObjectTypeInfo info = findGuiFor( type, version );
	if ( info.name.empty() )
		return NULL;
	return ObjectFactory::createTypeOf(info.name, info.version);
 }

IWidget* GuiUtilities::create_GUI( const std::string& name, const std::string& type, const URI& uri, const QStringList& interfaces ) {
	if ( interfaces.size() == 0 || ( interfaces.size() == 1 && interfaces.at(0) == type.c_str() ) ) {
		IWidget* guiWidget = createGuiFor<IWidget>(type, 0.0);
		if ( guiWidget != NULL ) {
			guiWidget->setTargetURI(uri);
			guiWidget->init();
			guiWidget->refreshAll();
			QWidget* widget = guiWidget->asQWidget();
			widget->setWindowIcon( GuiUtilities::createIconFor(type) );
		 }
		return guiWidget;
	 }
	int found = 0;
	for(int i=0; i<interfaces.size(); i++) {
		string cType = string(interfaces.at(i).toLatin1().data());
		GuiMappings::getMappings().transactionBeginRead();
		if ( GuiMappings::getMappings().exists(cType) ) {
			ObjectTypeInfo info = GuiMappings::getMappings().find( cType );
			if ( ObjectFactory::isAllocable(info.name, info.version) )
				++found;
		 }
		GuiMappings::getMappings().transactionEnd();
	 }
	if ( found == 0 ) return NULL;

	FormContainer* container = new FormContainer();
	container->setWindowTitle( name.c_str() );
	container->setWindowIcon( GuiUtilities::createIconFor(type) );
	if ( name != uri.toString() )
		container->setWindowTitle( QString("%1 [%2]").arg(name.c_str()).arg(uri.toString().c_str()) );
	QStringList alloc_interfaces;
	for(int i=0; i<interfaces.size(); i++) {
		ObjectTypeInfo info = findGuiFor(interfaces.at(i).toStdString(), 0.0);
		if ( info.name.empty() ) continue;
		if ( alloc_interfaces.contains( info.name.c_str() ) ) continue;// already added
		alloc_interfaces.push_back( info.name.c_str() );
		IWidget* iwidget = ObjectFactory::createTypeOf<IWidget>(info.name, info.version);
		if ( iwidget == NULL ) {
			//QMessageBox::information(0, "GUI Allocator", QString("Sorry, Can't allocate GUI for '%1'").arg(interfaces.at(i)) );
			continue;
		 }
		container->addIWidget(iwidget);
	 }
	container->setTargetURI(uri);
	container->init();
	container->refreshAll();
	return container;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void showWidget_default( QWidget* widget ) {
	if ( widget == NULL ) return;
	widget->show();
 }
GuiUtilities::WIDGET_SHOW widget_show_default = &showWidget_default;

void GuiUtilities::setShowWidgetMethod( WIDGET_SHOW fptr ) {
	if ( fptr == NULL )
		widget_show_default = &showWidget_default;
	else
		widget_show_default = fptr;
 }

void GuiUtilities::showWidget( QWidget* widget ) {
	if ( widget == NULL ) return;
	(*widget_show_default)(widget);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void GuiUtilities::list_GUI( std::vector<std::string>& types )
 {
	types.push_back(Table::className());
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

QVariant GuiUtilities::convertSerializable2QVariant( const wosh::ISerializable* object )
 {
	if ( object == NULL ) return QVariant();

	if ( object->isKindOf<Variant>() ) {
		const wosh::Variant* dataObject = dynamic_cast<const wosh::Variant*>(object);
		return GuiUtilities::convertVariant2QVariant(dataObject, Variant::STRING);
	 }
	else if ( object->isKindOf<List>() ) {
		const wosh::List* datalistObject = dynamic_cast<const wosh::List*>(object);
		QStringList slist;
		for(int i=0; i<datalistObject->size(); i++ ) {
			const Variant* vData = dynamic_cast<const Variant*>(datalistObject->itemAt(i));
			if ( vData == NULL ) continue;
			slist.append( vData->toString().c_str() );
		 }
		return QVariant(slist);
	 }
	else if ( object->isKindOf<URI>() ) {
		const wosh::URI* uriObject = dynamic_cast<const wosh::URI*>(object);
		return QVariant(uriObject->toString().c_str());
	 }
	return QVariant(object->toString().c_str());
 }


QVariant GuiUtilities::convertVariant2QVariant( const wosh::Variant* dataObject, Variant::DATA_TYPE default_type )
 {
	if ( dataObject == NULL ) return QVariant();

	if ( default_type != Variant::EMPTY )
		default_type = dataObject->getType();

	switch( default_type ) {
		case Variant::STRING:	return QVariant(dataObject->toString().c_str());
		case Variant::INTEGER:	return QVariant(dataObject->toInteger(0));
		case Variant::DOUBLE:	return QVariant(dataObject->toDouble(0.0));
//		case Variant::BINARY:	return QVariant(dataObject->toString().c_str());
		case Variant::BOOLEAN:	return QVariant(dataObject->toBoolean(false));
		case Variant::DATETIME: {
			QDateTime dt;
			dt.setTime_t( dataObject->toLong(0) );
			return QVariant(dt);
		 }
		case Variant::TIMESPAN: {
			QTime tm;
			tm = tm.addMSecs( dataObject->toLong(0) );
			return QVariant(tm);
		 }

		default:				return QVariant(dataObject->toString().c_str()); // let's be good
	 }
 }

void GuiUtilities::convertQVariant2Variant( wosh::Variant* dataObject, const QVariant& varData )
 {
	if ( dataObject == NULL ) return;
	dataObject->clear();
	if ( !varData.isValid() ) return;

	switch( varData.type() ) {
		case QVariant::String: {
			dataObject->set( varData.toString().toStdString() );
			break;
			   }
		case QVariant::Int: {
			dataObject->set( varData.toInt() );
			break;
			   }
		case QVariant::Double: {
			dataObject->set( varData.toDouble() );
			break;
			   }
		case QVariant::DateTime: {
			///@bug here
			dataObject->set( (long)varData.toDateTime().toTime_t(), Variant::DATETIME );
			break;
			   }
		case QVariant::ByteArray: {
			dataObject->set( varData.toString().toStdString() );
//			dataObject->setType(Variant::BINARY);
			break;
			   }
		default: break;
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

QIcon GuiUtilities::createIconFor( const string& objType, bool load_nearest ) {
	QString iconName = QString(objType.c_str()).replace("::", ".").append(".png");
	QDir icons( ":/icons/var/extras/icons/" );
	QIcon icon;
	if ( icons.exists(iconName) ) {
		icon.addPixmap(QPixmap(QString::fromUtf8(":/icons/var/extras/icons/").append(iconName)), QIcon::Normal, QIcon::Off);
	 }
	else if ( load_nearest ) {
		std::vector<ObjectTypeInfo> parents;
		WRESULT ret = ObjectFactory::gatherInfoOf(parents, objType, 0.0 );
		if ( WFAILED(ret) )
			return icon;
		std::vector<ObjectTypeInfo>::const_iterator it;
		for ( it=parents.begin(); it!=parents.end(); ++it ) {
			iconName = QString((*it).name.c_str()).replace("::", ".").append(".png");
			if ( icons.exists(iconName) ) {
				icon.addPixmap(QPixmap(QString::fromUtf8(":/icons/var/extras/icons/").append(iconName)), QIcon::Normal, QIcon::Off);
				break;
			 }
		 }
	 }
	return icon;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void GuiUtilities::promptMethodResponse( const Response* response, const QString& parent_title ) {
	QString msgstr = "";
	if ( WSUCCEEDED(response->getReturnValue()) ) {
		msgstr = QString("SUCCEEDED Executing method '%1'").arg(response->getMethod().c_str()).arg(response->getReturnValue());
	 }
	else if ( WFAILED(response->getReturnValue()) ) {
		msgstr = QString("FAILED#%1 Executing method '%2'.\n").arg(response->getReturnValue()).arg(response->getMethod().c_str());
		msgstr += QString("Generic error description is: ");
		msgstr += QString( Method::getGenericErrorDescription(response->getReturnValue()) );
		if ( response->hasData() ) {
			msgstr += QString("\nSpecific Error description:\n%1\n").arg(response->getData()->toString().c_str());
		 }
	 }
	char* buffer = NULL;
	QMessageBox msgBox;
	msgBox.setWindowTitle(QString("%1 Error").arg(parent_title));
	msgBox.setText(msgstr);
	if ( WSUCCEEDED(response->getReturnValue()) )
		msgBox.setIcon(QMessageBox::Information);
	else {
		msgBox.setIcon(QMessageBox::Warning);
		char* buffer = new char[12000];
		int64 size = PersistenceManager::serializeTo( response, "XML", buffer, 12000, true );
		if ( size <= 0 )
			msgBox.setDetailedText( "Serialziation Error!" );
		else
			msgBox.setDetailedText( buffer );
	}
	msgBox.resize( QSize(400, 270) );
	msgBox.exec();
	if ( buffer != NULL )
		delete [] buffer;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool GuiUtilities::warning( const QString& title, const QString& text ) {
	QMessageBox msgBox;
	msgBox.setWindowTitle("Warning..");
	msgBox.setText(title);
	msgBox.setInformativeText(text);
	msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
	msgBox.setIcon(QMessageBox::Warning);
	int ret = msgBox.exec();
	return ( ret == QMessageBox::Yes );
 }

void GuiUtilities::aboutWOSH( QWidget* parent ) {
	QString aboutTitle = "About WOSH..";
	if ( parent != NULL ) aboutTitle = parent->windowTitle();
	QMessageBox::information(parent, aboutTitle,
		"<h1>WOSH</h1>"
		"<h2>Wide Open Smart Home Framework</h2>"
		"<p>WOSH (Wide Open Smart Home) is an open source, multi-platform framework (message oriented middleware) written in ANSI C++, designed to enable (smart) home automation.</p>"
		"<p>WOSH is a Service Oriented Framework (SOA) providing a (Network) OS independent infrastructure for developing component based software (services, aka bundles), but it ships with many implemented services and some end-user applications (as woshsrv, WorkShop).</p>"
		"<p><br/>Refer to <a href='http://wosh.sourceforge.net'>http://wosh.sourceforge.net</a>.</p>"
		"<p>An open source software by <a href='mailto:contact@alessandropolo.name'>Alessandro Polo</a>.</p>"
		);
 }

void GuiUtilities::aboutError_woshconf( QWidget* parent, const std::string& cfgFile )
 {
	QString aboutTitle = "WOSH CRITICAL ERROR!";
	if ( parent != NULL ) aboutTitle = parent->windowTitle();
	QMessageBox::critical(parent, aboutTitle,
		QString(	"<h1>WOSH ERROR</h1>"
			"<h2>Configuration file is missing or invalid: %1</h2>"
			"<p>This file is required in order to setup WOSH Kernel and its modules.</p>"
			"<p>A sample configuration file is provided (/etc/wosh/wosh.default.conf)</p>"
			"<p>Refer to <a href='http://wosh.sourceforge.net/doc/page_config.html'>http://wosh.sourceforge.net/doc/</a>.</p>"
			"<p><br/>You have to select a configuration file or create one..</p>"
			).arg(cfgFile.c_str()) );


 }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace gui
}; // namespace wosh
